﻿using System;
using System.Diagnostics;

namespace Seagull.Generic
{
    [DebuggerDisplay("{ToString()}")]
    public struct Optional<T> : IEquatable<Optional<T>>
    {
        private readonly T _value;
        public T Value { get { return _value; } }

        private readonly bool _hasValue;
        public bool HasValue { get { return _hasValue; } }

        public bool IsSome { get { return _hasValue; } }
        public bool IsNone { get { return !_hasValue; } }

        public Optional(T value)
        {
            this._value = value;
            this._hasValue = true;
        }

        private static readonly Optional<T> _none = new Optional<T>();

        public static implicit operator Optional<T>(Optional optional)
        {
            return Optional<T>._none;
        }

        public T ValueOrDefault(Func<T> defaultValue = null)
        {
            return _hasValue ? _value :
                (defaultValue == null ? default(T) : defaultValue());
        }

        public override string ToString()
        {
            return string.Format("HasValue: {0}, Value: {1}", _hasValue, _value);
        }

        #region Equality members

        public bool Equals(Optional<T> other)
        {
            return Equals(other._value, _value) && other._hasValue.Equals(_hasValue);
        }

        public override bool Equals(object obj)
        {
            if(ReferenceEquals(null, obj))
                return false;
            if(obj.GetType() != typeof(Optional<T>))
                return false;
            return Equals((Optional<T>)obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                var hashCode = _hasValue.GetHashCode();
                if(_hasValue)
                    hashCode ^= _value.GetHashCode() * 397;
                return hashCode;
            }
        }

        public static bool operator ==(Optional<T> left, Optional<T> right)
        {
            return left.Equals(right);
        }

        public static bool operator !=(Optional<T> left, Optional<T> right)
        {
            return !left.Equals(right);
        }

        #endregion
    }

    public struct Optional
    {
        public static Optional<T> Some<T>(T value)
        {
            return new Optional<T>(value);
        }

        public static readonly Optional None = new Optional();
    }
}
